/*
 * SyntaxNode1.cpp
 *
 *  Created on: 28 Sep 2011
 *      Author: s0965328
 */

#include "SyntaxNode.h"
#include "SyntaxNodeIx.h"
#include "SyntaxNodeIDREF.h"
#include "ValueNode.h"
#include "ListNode.h"
#include "IDNode.h"
#include "util.h"

int SyntaxNode::use_global_names=0;
AmplModel *SyntaxNode::default_model =NULL;

extern int n_indexing;
extern SyntaxNodeIx *list_of_indexing[20];

/** Add an item to the back */
SyntaxNode *SyntaxNode::push_back(SyntaxNode *newitem)
{
  values.push_back(newitem);
  return this;
}

/** Add an item to the front */
SyntaxNode *SyntaxNode::push_front(SyntaxNode *newitem)
{
  int nval = values.size();
  values.resize(++nval);
  for (int i = nval; --i > 0; )
    values[i] = values[i - 1];
  values[0] = newitem;
  return this;
}


string SyntaxNode::print() const {
   ostringstream ost;
   ost << (*this);
   return ost.str();
}

void  SyntaxNode::dump(ostream& fout) const {
  fout << print_SyntaxNodesymb(this) << "\n";
}

/* ==========================================================================
SyntaxNode Methods to follow
============================================================================*/
// constructors:

SyntaxNode::SyntaxNode(const SyntaxNode &src) :
   opCode(src.opCode), values(src.values)
{
	LOG("Creating syntaxNode opCode["<<src.opCode<<"]");

}

SyntaxNode::SyntaxNode (int code, SyntaxNode *val1, SyntaxNode *val2, SyntaxNode* val3) :
   opCode(code) {
   int nval = 0;
   if(val1) nval++;
   if(val2) nval++;
   if(val3) nval++;

   values.resize(nval);

   int i = 0;
   if(val1) values[i++] = val1;
   if(val2) values[i++] = val2;
   if(val3) values[i++] = val3;

   LOG("SyntaxNode constructor  --- "<<nval<<"<- opCode["<<opCode<<"] val1:["<<val1<<"] val2:["<<val2<<"] val3:["<<val3<<"]");
}

SyntaxNode::~SyntaxNode() {

  for (int i = 0; i < nchild(); ++i)
    delete values[i];
}

/* --------------------------------------------------------------------------
SyntaxNode *SyntaxNode::deep_copy()
---------------------------------------------------------------------------- */
SyntaxNode *
SyntaxNode::deep_copy()
{
  SyntaxNode *newn = new SyntaxNode(opCode);

  if (opCode==IDREF || opCode==IDREFM){
    cerr << "IDREF SyntaxNodes need to be cloned differently\n";
    exit(1);
  }

  /* Values are copied depending on the type of the SyntaxNode */
  /* ID/IDREF/INT_VAL/FLOAT_VAL/IDREFM are treated differently */
  if (opCode==ID){
    cerr << "Called deep_copy for ID" << endl;
    throw exception();
  }

  for (SyntaxNode::iterator i = begin(); i != end(); ++i)
    newn->push_back((*i)->deep_copy());
  return newn;
}
/* --------------------------------------------------------------------------
SyntaxNode *SyntaxNode::clone()
---------------------------------------------------------------------------- */
SyntaxNode *
SyntaxNode::clone()
{
  SyntaxNode *newn = new SyntaxNode(opCode);

  if (opCode==IDREF){
    cerr << "IDREF SyntaxNodes need to be cloned differently\n";
    exit(1);
  }
  newn->values = values;

  return newn;
}

/* --------------------------------------------------------------------------
char *SyntaxNode::printDummyVar()
---------------------------------------------------------------------------- */
/* Assumes that the current SyntaxNode is the dummy variable in an indexing
   expression: that is it, is either ID or LBRACKET (COMMA (ID1 .. IDn)) */

string
SyntaxNode::printDummyVar() const {
  if (opCode==ID){
    return this->print();
  }else{
    SyntaxNode *list;
    // this must be LBRACKET
    if (opCode!=LBRACKET){
      cerr << "printDummyVar: dummy var must be ID or (ID1,..,IDn)\n";
      cerr << "current opCode is "+opCode;
      exit(1);
    }
    list = front();
    if (list->opCode==ID) return list->print();
    if (list->opCode!=COMMA){
      cerr << "printDummyVar: dummy var must be ID or (ID1,..,IDn)\n";
      cerr << "current opCode is "+list->opCode;
      exit(1);
    }
    return list->print();
  }
}

/* --------------------------------------------------------------------------
SyntaxNode::findIDREF(list<ModelComp> *lmc)
---------------------------------------------------------------------------- */
/** Find all the IDREF nodes at or below the current node */
void
SyntaxNode::findIDREF(list<ModelComp*>& lmc) const {

  if (opCode == ID)
    return;

  if (opCode==IDREF){
    //printf("%s\n",getGlobalName((ModelComp*)this->values[0],
    //				NULL, NULL, NOARG));
    lmc.push_back(((SyntaxNodeIDREF*)this)->ref);
  }else{
    for (SyntaxNode::iterator i = begin(); i != end(); ++i)
      if (*i)
        (*i)->findIDREF(lmc);
  }
}

/* --------------------------------------------------------------------------
SyntaxNode::findIDREF(list<SyntaxNode *> *lnd)
---------------------------------------------------------------------------- */
/** Find all the IDREF nodes at or below the current node */
void
SyntaxNode::findIDREF(list<SyntaxNode*> *lnd) {

  findOpCode(IDREF, lnd);
}

/* --------------------------------------------------------------------------
SyntaxNode::findOpCode(int oc, list<SyntaxNode *> *lnd)
---------------------------------------------------------------------------- */
/** Find all nodes of opCode @a oc at or below the current node */
void
SyntaxNode::findOpCode(int oc, list<SyntaxNode*> *lnd) {

  // if terminal then return
  if (opCode == ID)
    return;

  if (opCode==oc){
    //printf("%s\n",getGlobalName((ModelComp*)this->values[0],
    //				NULL, NULL, NOARG));
    lnd->push_back(this);
  }else{
    for (SyntaxNode::iterator i = begin(); i != end(); ++i)
      if (*i)
        (*i)->findOpCode(oc, lnd);
  }
}

/* --------------------------------------------------------------------------
SyntaxNode::findModelComp()
---------------------------------------------------------------------------- */
/** Find the ModelComp (if it exists) referred to by this SyntaxNode.
 *
 *  @return The ModelComp only if the expression given by this SyntaxNode is
 *          an immediate reference to a ModelComp, otherwise NULL.
 */
ModelComp *SyntaxNode::findModelComp() const {
  const SyntaxNode *on = this;
  while ((on->opCode==LBRACKET || on->opCode==LBRACE) && on->nchild() == 1) {
    on = on->values[0];
  }

  if (opCode==IDREF){
    const SyntaxNodeIDREF *onref = dynamic_cast<const SyntaxNodeIDREF*>(this);
    return onref->ref;
  }
  return NULL;
}

/* --------------------------------------------------------------------------
SyntaxNode::getArgumentList()
---------------------------------------------------------------------------- */
/** This is for a SyntaxNode of type IDREF (and should eventually be moved
 *  to SyntaxNodeIDREF:getArgumentList()).
 *
 *  @return A comma separated list of the arguments (the bit in [..] brackets).
 */
string
SyntaxNode::getArgumentList() const
{
   const SyntaxNodeIDREF *on;
   string arglist = "";
   if (getOpCode() != IDREF) {
      cerr << "Can only call getArgumentList for SyntaxNodes of type IDREF\n";
      exit(1);
   }

   // see if this is actually an IDREF node
   on = dynamic_cast<const SyntaxNodeIDREF*>(this);
   if (on==NULL){
      cout << "WARNING: This is an IDREF SyntaxNode not of type SyntaxNodeIDREF\n";
      if (nchild() > 0) {
         arglist += values[1]->print();
         for (int i = 1; i < nchild(); ++i)
           arglist += "," + values[1+i]->print();
      }
   }else{
     if (nchild() > 0) {
         SyntaxNode::iterator i = begin();
         arglist += (*i)->print();
         while (++i != end())
           arglist += "," + (*i)->print();
      }
  }
  return arglist;
}

/** Merges the values list of src into that of this object.
 *
 *  The items from src are prepended to this object's values.
 */
SyntaxNode* SyntaxNode::merge(const SyntaxNode *src) {

   int nval = values.size();
   int srcnval = src->nchild();
   values.resize(srcnval + nval);

   // copy this object's values to the end
   for (int i = srcnval + nval; --i > 0; )
     values[i] = values[i - srcnval];

   // copy src's values to the beginning
   for (int i = 0; i < srcnval; ++i)
     values[i] = src->values[i];

   return this;
}


ostream& SyntaxNode::put(ostream&s) const {
  static int level=0;

  if(this == NULL) return s;

  SyntaxNode::iterator i = this->begin();
  /*if(s!=cout) {
     for(int j=0; j<level; ++j) cout << " ";
     if(level!=0) cout << "-";
     level++;
     cout << "here " << this->getOpCode() << "(" << node << ")\n";
  }*/

  switch (this->getOpCode()) {
    case 0:          s << **i;                           break;
      /* these are lots of simple binary operators */
    case ID:
                     //s << (const char*)*i;               break;
      cerr << "ID put bad." << endl;
      throw exception();
      break;
    case ' ':
      if (this->nchild() > 1)
        s << **(i++);
      s << ' ' << **i;
      break;
    case DOT:
      s << **i << ".";
      s << **(++i);
      break;
    case COMMA:
      s << **i;
      while (++i != end())
         s << "," << (**i);
      break;
    case DIFF:
      if (this->nchild() > 1)
        s << **(i++);
      s << " diff " <<  **i;
      break;
    case CROSS:
      if (this->nchild() > 1)
        s << **(i++);
      s << " cross " <<  **i;
      break;
    case DOTDOT:
      if (this->nchild() > 1)
        s << **(i++);
      s << " .. " <<  **i;
      break;
    case SUM:
      s << "sum " << **i;
      s << **(++i);
      break;
    case MAX:
      s << "max " << **i;
      s << **(++i);
      break;
    case MIN:
      s << "min " << **i;
      s << **(++i);
      break;
    case EXPECTATION:
      s << "Exp( " << **i << ")";
      break;
    case LAST:
      s << "last( " << **i << ")";
      break;
    case FIRST:
      s << "first( " << **i << ")";
      break;
      // -------------------------functions f(..) --------------------------
    case ORD:
      s << "ord" << **i;
      break;
      // -------------------------terminals --------------------------
    case ORDERED:       s << "ordered";        break;
    case SYMBOLIC:      s << "symbolic";       break;
    case DETERMINISTIC: s << "deterministic";  break;
      /* these are lots of simple unary operators */
    case WITHIN:
      s << "within " << **i;
      break;
    case LSBRACKET:
      if (this->nchild() == 1)
         s << "[" << **i << "]";
      else {
         s << **i << "[";
         s << **(++i) << "]";
      }
      break;
    case LBRACE:
      s << "{" << *i << "}";
      break;
    case LBRACKET:
      s << "(" << **i << ")";
      break;
    case COLON:
      if (this->nchild() > 1)
        s << **(i++);
      s << ": " <<  **i;
      break;
    case IF:
      s << "if " << **i;
      s << " then " << **(++i);
      if (this->nchild() == 3)
        s << " else " << **(++i);
      break;
    case IDREF:
    case IDREFM:
      const SyntaxNodeIDREF *onidref;
      if(!(onidref = (const SyntaxNodeIDREF*)(this))) {
         cerr << "Cast of node to SyntaxNodeIDREF failed!\n";
         exit(1);
      }
      s << onidref;
      break;
    case -99: // template<class T> ValueNode
      cerr << "FAIL(-99)";
      throw exception();
      break;
    default:
      s << endl;
      cerr << "Unknown opcode " << this->getOpCode() << "\n";
      cerr << ".nval = " << this->nchild() << "\n";
      for(; i!=this->end(); ++i) {
         cerr << "val[" << **i << "]\n";
      }
      throw exception();
      exit(1);
    }
  if(s!=cout) level--;
  return s;
}

//Feng

int SyntaxNode::calculateSetDim()
{
	LOG(this->print());
	int dim = 0;
	if(this->opCode == DEFINED)
	{//OpNode
		assert(this->values[0] != NULL);
		assert(this->values[1] == NULL);
		dim = this->values[0]->calculateSetDim();
	}
	else if(this->opCode == DIFF)
	{//CompositeSet
		assert(this->values[0] != NULL && this->values[1] != NULL);
		dim = this->values[0]->calculateSetDim();
	}
	else if(this->opCode == CROSS)
	{//CompositeSet
		assert(this->values[0] != NULL && this->values[1] != NULL);
		dim = this->values[0]->calculateSetDim() + this->values[1]->calculateSetDim();
	}
	else if(this->opCode == IDREF)
	{//SyntaxNodeIDREF
		SyntaxNodeIDREF* nodeIdRef = static_cast<SyntaxNodeIDREF*>(this);
		dim = nodeIdRef->ref->getSetDim();
	}
	else if(this->opCode == ID)
	{//IDNode
		dim = 1;
	}
	else if(this->opCode == LBRACE)
	{//ListSet  a,b,c,d
		dim = this->values[0]->calculateSetDim();
		assert(this->values[1]==NULL);
	}
	else if(this->opCode == COMMA)
	{//ListNode
		dim = this->values[0]->calculateSetDim();
		dim += this->values[1]!=NULL?this->values[1]->calculateSetDim():0;
		dim += this->values[2]!=NULL?this->values[2]->calculateSetDim():0;
		dim += this->values[3]!=NULL?this->values[3]->calculateSetDim():0;
		assert(this->values[4]==NULL);
	}
	else if(this->opCode == DOTDOT)
	{//SimpleSet
		dim = 1;
	}
	else if(this->opCode == ORDERED)
	{//ordered property -- will not use without ampl
		dim = 1;
	}
	else
	{
		LOG("opCode["<<this->opCode<<"] was not yet implemented --- calculateSetDim");
		assert(false);
	}

	return dim;
}


int SyntaxNode::calculateNumParamIndicies()
{
	int num = 0;
	LOG("opCode["<<this->opCode<<"]  "<<this->print());
	if(this->opCode == IDREF)
	{
		SyntaxNodeIDREF* nodeIdRef = static_cast<SyntaxNodeIDREF*>(this);
		num = nodeIdRef->ref->getSetDim();
	}
	else if(this->opCode == LBRACE)
	{
		LOG("this ListNode size["<<this->values.size()<<"]");
		assert(this->values.size()==1);
		num = this->values[0]->calculateNumParamIndicies();
	}
	else if(this->opCode == COMMA)
	{
		LOG("this ListNode size["<<this->values.size()<<"]");
		vector<SyntaxNode*>::iterator i;
		for(i=this->values.begin();i!=this->values.end();i++)
		{
			num += (*i)->calculateNumParamIndicies();
		}
	}
	else if(this->opCode == IN)
	{
		assert(this->values[0]!=NULL);
		num = this->values[1]->calculateNumParamIndicies();
		assert(this->values.size()==2);
	}
	else
	{
		LOG("opCode["<<this->opCode<<"] was not yet implemented -- calculateNumParamIndicies");
		assert(false);
	}
	return num;
}

//End Feng

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/* --------------------------------------------------------------------------
SyntaxNode::print()
--------------------------------------------------------------------------- */
/** Recursively prints the expression rooted at the current node in the
 *  expression tree.
 *
 *  @note use_global_names influences how nodes of type IDREF are printed.
 */
ostream&
operator<<(ostream&s, const SyntaxNode *node) {
   if(node == NULL) return s;
   return node->put(s);
}

ostream&
operator<<(ostream&s, const SyntaxNode &node) {
	if(&node == NULL) return s;
   return node.put(s);
}



string print_SyntaxNodesymb(const SyntaxNode *node) {
  const ValueNode<long> *inode;
  const ValueNode<double> *dnode;
  ostringstream  ost;

  if (node==NULL){
    return "NULL";
  }
  if (node->getOpCode() == ID)
    return "(ID T)";
  if ((inode = dynamic_cast<const ValueNode<long> *>(node))) {
    string temp = "T:";
    temp += inode->getValue();
    return temp;
  }
  if ((dnode = dynamic_cast<const ValueNode<double> *>(node))) {
    string temp = "T:";
    temp += dnode->getValue();
    return temp;
  }

  // start new version
  // print node symbol
  switch (node->getOpCode()) {
  case IDREF: {
    const SyntaxNodeIDREF *onir= dynamic_cast<const SyntaxNodeIDREF*>(node);
    if (onir==NULL) {
      cerr << "Some IDREF node still not SyntaxNodeIDREF\n";
      exit(1);
    }
    ModelComp *mc = onir->ref;
    ost << "IDREF(" << (void*)node << ":" << mc->id << "(" << (void*) mc <<"))";
  }
  break;
  case ASSIGN: ost << "ASSIGN"; break;
  case IN:     ost << "IN"; break;
  case SUM:    ost << "SUM"; break;
  case LBRACE: ost << "LBR{"; break;
  case LBRACKET:ost << "LBR("; break;
  case COMMA:  ost << "COMMA"; break;
  case COLON:  ost << "COLON"; break;
  case '+':    ost << "\"+\""; break;
  case '*':    ost << "\"*\""; break;
  default:
    ost << "\"" << node->getOpCode() << "\"";
  }
  ost << "(";
  for(SyntaxNode::iterator i=node->begin(); i!=node->end(); ++i){
    if(i!=node->begin()) ost << ", ";
    ost << print_SyntaxNodesymb(*i);
  }
  ost << ")";

  return ost.str();
}



/* ----------------------------------------------------------------------------
findKeywordinTree
---------------------------------------------------------------------------- */
/** Traverses down the tree and returns the topmost reference to the keyword
 *  in the Tree.
 */
SyntaxNode *
findKeywordinTree(SyntaxNode *root, int oc)
{
  if (root->getOpCode() == oc)
    return root;

   SyntaxNode *found, *res;
   found = NULL;
   for(SyntaxNode::iterator i=root->begin(); i!=root->end(); ++i) {
      res = findKeywordinTree(*i, oc);
      if(res && found) {
         cerr << "Found keyword " << oc << "at least twice in " << root << "\n";
         exit(1);
      }
      found = res;
   }
   return found;
}

/* ---------------------------------------------------------------------------
find_var_ref_in_context
---------------------------------------------------------------------------- */
/** This routine does the work of putting together dot'd variable names
 *  'root' is a SyntaxNode of type ID that points to the left hand part
 *  of the dot'd expression parsed so far. 'ref' is the new part that
 *  should be added.
 *
 *  @param context
 *         A pointer to the current AmplModel that defines the scope in which
 *         the ID expressions should be resolved.
 *  @param ref
 *         A pointer to an expression that evaluates to a ModelComp: this can
 *         be given by an ID, a dotted expression ID.ID or a reference to a
 *         parent stage (in StochProg) such as ID(-1;...).
 *         It can also carry an indexing expressinon ID[.,.,.] in which case
 *         the indexing is attached to the returned IDREF node.
 *
 *  @return A SyntaxNode of type IDREF that points to the correct ModelComp.
 *
 *  @bug Should return a SyntaxNodeIDREF*.
 *
 *  A SyntaxNode of type IDREF looks like this
 *       ->opCode = IDREF;
 *       ->nval = # of arguments
 *       ->values[0] = pointer to entity in model list
 *       ->values[1 - n] = arguments
 */
SyntaxNode*
find_var_ref_in_context(AmplModel *context, SyntaxNode *ref)
{
   /* 'ref' is a SyntaxNode representing an iditem.
      This can be either
      - a ID node where values[0] simply points to a name
      - an ID node which is actually SyntaxNodeID and has stochparent set
      - a LSBRACKET node, where values[0] is ID and values[1] is CSL
      in the second case the CSL should be added as further arguments
      to the resulting IDREF node
   */

   /* returns: pointer */
	LOG("Finding ref:"<<ref<<" in AmplModel(context):"<<context->name);
   SyntaxNode *tmp;
   ListNode *argNode;
   IDNode *idNode;
   SyntaxNodeIDREF *ret;
   int stochparent=0;

   /* and now scan through the whole of the local context to see if we
      find any matches */
   /* the local context is
       - all vars
       - all constraints
       - all objectives
       - all sets
       - all parameters
       - all submodels
       - all temporary variables (this list needs to be set up somewhere)
   */

   // split the expression 'ref' into an id part and an argument list

   if (ref->getOpCode() == ID) {
	   LOG("  -- ["<<ref<<"] has an ID opCode");
      idNode = (IDNode *)ref;
      argNode = NULL;
   }else{
      assert(ref->getOpCode() == LSBRACKET || ref->getOpCode() == LBRACKET);
	   LOG("  -- ["<<ref<<"] has an LSBRACKET or LBRACKET opCode");
      SyntaxNode::iterator i = ref->begin();
      idNode = (IDNode*)*i;
      argNode = (ListNode*) *(++i);
      assert(idNode->getOpCode() == ID);
      assert(argNode->getOpCode() == COMMA);
   }

   // Test if this ID node is actually of type SyntaxNodeID and if so remember
   // the value of stochparent
   {
     if (idNode->getStochParent() != 0)
         // there is an extra argument, which is the stochparent
       stochparent = idNode->getStochParent();
   }

  LOG("--> now Search for matches of " << idNode->id() <<" in current active indexings");

   // see if this matches a dummy variable
   tmp = find_var_ref_in_indexing(idNode->id());
   if (tmp) {
      LOG("    "<<idNode->id() << " is matched by dummy var in " << *tmp);
      return ref;
   }

   // try to find a match in the local context
   LOG("--> continue searching in this_context["<<context->name<<"]");
   ret = context->find_var_ref_in_context(idNode);

   // ret could be NULL if it is actually a STAGE or NODE dummy variable
	if(!ret) {
		LOG("--> not found in either current index or current context's modelComp");
		if(argNode) {
			cerr << "dummy index of stageset or nodeset and argNode=true not "
			"yet handled!" << endl;
			exit(1);
		}
		return idNode; // return something at least vaguely meaningful
	}

   if (argNode){
      LOG( "Adding argument list to node: " << *argNode);
      //free(idNode->values); // jdh - what does this do?
      for (ListNode::iterator i = argNode->begin(); i != argNode->end(); ++i)
         ret->push_back(*i);
      if (ref->getOpCode() == LBRACKET) {
         // this is old code to deal with ancestor(1).ID declarations. To go
         cerr << "Executing old code to deal with ancestor(1).ID "
            "declarations\n";
         exit(1);

         // This is a reference indexed by '(..)'. In this case we are in
         // a stoch block and the first argument refers to the stage
         //      ret->stochrecourse = (SyntaxNode*)ret->values[0];
         //for (int i = 1; i < ret->nchild(); ++i) {
         //ret->values[i-1] = ret->values[i];
         //}
         //ret->nval--;
      }
      argNode->clear();
   }

   ret->setStochParent(stochparent);
   return ret;
}

/* ---------------------------------------------------------------------------
find_var_ref_in_indexing
---------------------------------------------------------------------------- */
/** Scan through the current set of active indexing expressions and see if
 *  any of them defines the dummy variable given by 'name'.
 *
 *  @param name
 *         The name of identifier to look for.
 *
 *   int n_indexing             the currently active indexing expressions
 *   SyntaxNode *list_of_indexing
 *  @return The Indexing expression in which the name occurs (or NULL if there
 *          is no match).
 */
SyntaxNode *
find_var_ref_in_indexing(const string& name) {

   SyntaxNodeIx *tmp;
   SyntaxNode *ret = NULL;

   for (int i = 0; i < n_indexing; ++i) {
      /* have a look at all the indexing expressions */
      /* an indexing expression is a '{' node followed by a 'Comma' node */
      tmp = list_of_indexing[i];
      if (tmp!=NULL){
         tmp->splitExpression();
         ret = tmp->hasDummyVar(name);
         if (ret) return ret;
      }
   }
   return ret;
}

